//////////////////////////////////////////////
// TestClass.h
// 
//////////////////////////////////////////////

/// Defines / Macros -------------------------

#pragma once

/// Forward decl -----------------------------

namespace nkDebug
{
	class TestRunParameters ;

	struct ClassInitFunctionDescriptor ;
	struct ClassShutdownFunctionDescriptor ;
}

/// Includes ---------------------------------

// nkDebug
#include "../Dll/DllDefines.h"

#include "FunctionSet.h"
#include "UnitTester.h"

// nkMemory
#include <NilkinsMemory/Containers/String.h>
#include <NilkinsMemory/Containers/StringView.h>

/// Class ------------------------------------

namespace nkDebug
{
	class DLL_NKDEBUG_EXPORT TestClass
	{
		public :

			// Constructor, destructor
			TestClass () noexcept ;
			TestClass (nkMemory::StringView name) noexcept ;
			virtual ~TestClass () ;

			// Getters
			FunctionSet& getFunctionSet () ;
			const FunctionSet& getFunctionSet () const ;
			std::function<bool ()> getClassInitFunction () const ;
			std::function<void ()> getClassShutdownFunction () const ;
			std::function<bool ()> getFunctionInitFunction () const ;
			std::function<void ()> getFunctionShutdownFunction () const ;
			nkMemory::StringView getName () const ;

			// Setters
			bool setClassInitFunction (std::function<bool ()> func) ;
			bool setClassShutdownFunction (std::function<void ()> func) ;
			bool setTestFunctionInitFunction (std::function<bool ()> func) ;
			bool setTestFunctionShutdownFunction (std::function<void ()> func) ;
			void setName (nkMemory::StringView value) ;

			// Execution
			void execute (const TestRunParameters& parameters) const ;

		protected :

			// Attributes
			// Functions
			FunctionSet _functionSet ;
			ClassInitFunctionDescriptor* _classInitFunction ;
			ClassShutdownFunctionDescriptor* _classShutdownFunction ;
			ClassInitFunctionDescriptor* _testFunctionInitFunction ;
			ClassShutdownFunctionDescriptor* _testFunctionShutdownFunction ;

			// Tracking name
			nkMemory::String _name ;
	} ;

	static const char NK_DEBUG_STATIC_EMPTY_CHAR [] = "" ;

	template <typename T, const char* T_NAME>
	class RegisteredTestClass : public TestClass
	{
		public :

			// Functions
			// Constructor
			RegisteredTestClass ()
			:	TestClass (T_NAME)
			{
				// Nothing to do
			}

			// Factory
			static TestClass* factoryFunc ()
			{
				return new T () ;
			}

		public :

			// Attributes
			// Registered class pointer
			inline static TestClass* _registered = UnitTester::registerTestClass(&factoryFunc) ;
	} ;
}

/// Utility macros ---------------------------

#define NK_TEST_CLASS(name)	\
	static const char NK_DEBUG_STATIC_CHAR_##name [] = #name ; \
	class name : public nkDebug::RegisteredTestClass<name, NK_DEBUG_STATIC_CHAR_##name>

#define NK_TEST_CLASS_NS(ns, name) \
	namespace ns \
	{ \
		static const char NK_DEBUG_STATIC_CHAR_##name [] = #ns "::" #name ; \
		class name : public nkDebug::RegisteredTestClass<name, NK_DEBUG_STATIC_CHAR_##name> \

#define NK_TEST_CLASS_NS_END }

#define NK_TEST_FUNCTION(name) \
	static void caller_##name () \
	{ \
		name() ; \
	} \
	\
	inline static const std::function<void ()> registered_##name = _registered->getFunctionSet().registerFunction(#name, caller_##name) ; \
	\
	static void name () \

#define NK_TEST_CLASS_INIT \
	static bool classInitFunctionCaller () \
	{ \
		return classInitFunction() ; \
	} \
	\
	inline static const bool classInitRegistered = _registered->setClassInitFunction(classInitFunctionCaller) ; \
	\
	static bool classInitFunction () \

#define NK_TEST_CLASS_SHUTDOWN \
	static void classShutdownFunctionCaller () \
	{ \
		classShutdownFunction() ; \
	} \
	\
	inline static const bool classShutdownRegistered = _registered->setClassShutdownFunction(classShutdownFunctionCaller) ; \
	\
	static void classShutdownFunction () \

#define NK_TEST_FUNCTION_INIT \
	static bool testFunctionInitFunctionCaller () \
	{ \
		return testFunctionInitFunction() ; \
	} \
	\
	inline static const bool testFunctionInitRegistered = _registered->setTestFunctionInitFunction(testFunctionInitFunctionCaller) ; \
	\
	static bool testFunctionInitFunction () \

#define NK_TEST_FUNCTION_SHUTDOWN \
	static void testFunctionShutdownFunctionCaller () \
	{ \
		testFunctionShutdownFunction() ; \
	} \
	\
	inline static const bool testFunctionShutdownRegistered = _registered->setTestFunctionShutdownFunction(testFunctionShutdownFunctionCaller) ; \
	\
	static void testFunctionShutdownFunction () \
